package com.ibeeking.found.msg.rest.service.impl;

import com.ibeeking.found.common.config.secret.RsaConfig;
import com.ibeeking.found.common.constants.WxCpServiceTypeConstant;
import com.ibeeking.found.common.constants.WxOpenServiceTypeConstant;
import com.ibeeking.found.common.entity.open.*;
import com.ibeeking.found.common.enums.OpenApiConfigEnum;
import com.ibeeking.found.common.enums.WxCpAppTypeEnum;
import com.ibeeking.found.common.enums.WxOpenAppTypeEnum;
import com.ibeeking.found.common.enums.sms.SmsVendorEnum;
import com.ibeeking.found.common.response.ResponseCodeEnum;
import com.ibeeking.found.common.utils.DataUtils;
import com.ibeeking.gmc.global.api.feign.GlobalOpenFeignClient;
import com.ibeeking.found.msg.rest.config.prop.RedisProperties;
import com.ibeeking.found.msg.rest.service.IEmailConfigService;
import com.ibeeking.found.msg.rest.service.ISmsConfigService;
import com.ibeeking.found.msg.rest.service.IOpenService;
import com.ibeeking.found.msg.service.common.bo.EmailConfigSslBO;
import com.ibeeking.found.msg.service.common.bo.SmsConfigBO;
import com.ibeeking.found.msg.service.common.bo.SmsConfigByAliBO;
import com.ibeeking.found.msg.service.common.bo.SmsConfigByTecentBO;
import com.ibeeking.found.msg.service.common.constants.RedisKeyConstant;
import com.ibeeking.found.msg.service.common.vo.EmailConfigVO;
import com.ibeeking.nematos.core.utils.SpringBeanUtil;
import com.ibeeking.nematos.exception.BusinessException;
import com.ibeeking.nematos.exception.DataNotExistException;
import com.ibeeking.nematos.log.utils.LogUtils;
import com.ibeeking.nematos.msg.email.core.config.EmailSenderProperties;

import com.ibeeking.nematos.msg.email.core.sender.PooledEmailSender;

import com.ibeeking.nematos.msg.email.core.service.EmailService;
import com.ibeeking.nematos.msg.email.core.service.impl.EmailServiceImpl;
import com.ibeeking.nematos.msg.sms.spring.autoconfigure.SmsAutoConfigure;
import com.ibeeking.nematos.msg.sms.spring.entity.SmsProperties;
import com.ibeeking.nematos.msg.sms.spring.service.SmsService;

import com.ibeeking.nematos.redis.utils.RedisUtils;
import com.ibeeking.nematos.utils.cryptor.PasswordCryptor;
import com.ibeeking.nematos.utils.cryptor.SimplePasswordCryptor;
import com.ibeeking.nematos.utils.data.StringUtils;
import com.ibeeking.nematos.utils.encryption.EncryptUtils;
import com.ibeeking.nematos.utils.json.JsonUtils;
import com.ibeeking.nematos.utils.result.ResponseResult;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.config.impl.WxCpJedisConfigImpl;
import me.chanjar.weixin.open.api.WxOpenComponentService;
import me.chanjar.weixin.open.api.WxOpenMaService;
import me.chanjar.weixin.open.api.WxOpenMpService;
import me.chanjar.weixin.open.api.WxOpenService;
import me.chanjar.weixin.open.api.impl.WxOpenInRedisConfigStorage;
import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.security.PrivateKey;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * 禁止在此类加其他业务方法
 *
 * @ClassName WxMpService
 * @Description
 * @Author ibeeking
 * @Date 2020-12-30 10:29
 **/
@Service
@EnableConfigurationProperties({RedisProperties.class})
public class OpenServiceImpl implements IOpenService {
    private Logger logger = LoggerFactory.getLogger(getClass());
    //30分钟
    private static final Long TIME = 30 * 60 *1000L;

    private static Map<String, EmailSenderProperties> senderPropertiesMap = new HashMap<>();

    @Resource
    private RsaConfig rsaConfig;

    @Resource
    private RedisUtils redisUtils;

    @Autowired
    private RedisProperties redisProperties;

    @Resource
    private GlobalOpenFeignClient globalOpenFeignClient;

    @Resource
    private ISmsConfigService smsConfigServiceImpl;

    @Resource
    private IEmailConfigService emailConfigServiceImpl;

    @Override
    public OpenConfigDTO queryOpenConfigOfWxOpen() {
        OpenConfigQuery query = new OpenConfigQuery();
        query.setType(OpenApiConfigEnum.OPEN_CONFIG_TYPE_WECHAT_OPEN.getCode());
        query.setSubType(WxOpenAppTypeEnum.WECHAT_OPEN_APP_TYPE_TP.getCode());
        ResponseResult<OpenConfigDTO> responseResult = globalOpenFeignClient.queryOpenConfigOne(query);
        OpenConfigDTO openConfigDTO = responseResult.getData();
        return openConfigDTO;
    }

    @Override
    public OpenConfigDTO queryOpenConfigOfWxCpSb() {
        OpenConfigQuery query = new OpenConfigQuery();
        query.setType(OpenApiConfigEnum.OPEN_CONFIG_TYPE_WECHAT_CP.getCode());
        query.setSubType(WxCpAppTypeEnum.WECHAT_CP_APP_TYPE_SB.getCode());
        ResponseResult<OpenConfigDTO> responseResult = globalOpenFeignClient.queryOpenConfigOne(query);
        OpenConfigDTO openConfigDTO = responseResult.getData();
        return openConfigDTO;
    }

    @Override
    public OpenAuthDTO queryWxOpenAuthByTenantId(Long tenantId,Integer configType, Integer configSubType, Integer appType) {
        OpenAuthQuery query = new OpenAuthQuery();
        query.setTenantId(tenantId);
        query.setConfigType(configType);
        query.setConfigSubType(configSubType);
        query.setAppType(appType);
        ResponseResult<OpenAuthDTO> responseResult = globalOpenFeignClient.queryOpenAuthOne(query);
        DataUtils.trueThrowException(!ResponseCodeEnum.SUCCESS.getCode().equals(responseResult.getCode()), responseResult.getMsg());
        return responseResult.getData();
    }

    @Override
    public OpenAuthDTO queryWxOpenAuthByTenantIdAndAppId(Long tenantId, String appId){
        OpenAuthQuery query = new OpenAuthQuery();
        query.setTenantId(tenantId);
        query.setAppId(appId);
        ResponseResult<OpenAuthDTO> responseResult = globalOpenFeignClient.queryOpenAuthOne(query);
        DataUtils.trueThrowException(!ResponseCodeEnum.SUCCESS.getCode().equals(responseResult.getCode()), responseResult.getMsg());
        return responseResult.getData();
    }

    /**
     * 获取公众号服务并将注册为bean
     *
     * @param
     * @return
     */
    @Override
    public WxOpenMpService getWxOpenMpService(Long tenantId) {
        WxOpenService wxOpenService = SpringBeanUtil.getBean(WxOpenService.class);
        if(wxOpenService == null) throw new BusinessException("get WxOpenService service error!");
        WxOpenInRedisConfigStorage redisConfigStorage = null;
        String configStoragekey = RedisKeyConstant.WX_OPEN_CONFIG_KEY.concat(tenantId.toString()).concat(":").concat(WxOpenServiceTypeConstant.APPID_TYPE_MP.toString());
        String appId = null;
        Object objConfigStorage = redisUtils.get(configStoragekey);
        if(ObjectUtils.isEmpty(objConfigStorage)) {
            redisConfigStorage = new WxOpenInRedisConfigStorage(redisProperties.getJedisPool(), "wx:open");
            OpenConfigDTO wxOpenConfig = queryOpenConfigOfWxOpen();
            if(wxOpenConfig == null) throw new BusinessException("没有读取到开放平台配置参数!");
            OpenConfigOfExtend openConfigOfExtend = wxOpenConfig.getOpenConfigOfExtend();
            if(openConfigOfExtend == null) throw new BusinessException("没有读取到开放平台配置参数!");
            OpenConfigOfWxOpen openConfigOfWxOpen = openConfigOfExtend.getOpenConfigOfWxOpen();
            if(openConfigOfWxOpen == null) throw new BusinessException("没有读取到开放平台配置权限!");
            OpenAuthDTO openAuthDTO = queryWxOpenAuthByTenantId(tenantId, wxOpenConfig.getType(), wxOpenConfig.getSubType(), WxOpenServiceTypeConstant.APPID_TYPE_MP);
            if(openAuthDTO == null) throw new BusinessException("没有读取到开放平台配置权限!");
            OpenAuthOfExtend openAuthOfExtend = openAuthDTO.getOpenAuthOfExtend();
            if(openAuthOfExtend == null) throw new BusinessException("没有读取到开放平台配置权限!");
            appId = openAuthDTO.getAppId();
            OpenAuthOfWxOpen openAuthOfWxOpen = openAuthOfExtend.getOpenAuthOfWxOpen();
            if(openAuthOfWxOpen == null) throw new BusinessException("没有读取到开放平台配置权限!");
            redisConfigStorage.setComponentAppId(wxOpenConfig.getComponentAppId());
            redisConfigStorage.setComponentAppSecret(wxOpenConfig.getOpenAppSecret());
            redisConfigStorage.setComponentToken(wxOpenConfig.getComponentMsgToken());
            redisConfigStorage.setComponentAesKey(wxOpenConfig.getComponentMsgAesKey());
            redisConfigStorage.setComponentVerifyTicket(openConfigOfWxOpen.getComponentVerifyTicket());
            redisConfigStorage.setComponentAccessToken(openConfigOfWxOpen.getComponentAccessToken());
            redisConfigStorage.updateAuthorizerRefreshToken(appId, openAuthOfWxOpen.getAuthorizerRefreshToken());
            WxOpenAuthorizerAccessToken authorizerAccessToken = new WxOpenAuthorizerAccessToken();
            authorizerAccessToken.setAuthorizerRefreshToken(openAuthOfWxOpen.getAuthorizerRefreshToken());
            authorizerAccessToken.setAuthorizerAccessToken(openAuthOfWxOpen.getAuthorizerAccessToken());
            redisConfigStorage.updateAuthorizerAccessToken(appId,authorizerAccessToken);
            redisUtils.set(configStoragekey, redisConfigStorage, 1000);
        }else{
            redisConfigStorage = (WxOpenInRedisConfigStorage) objConfigStorage;
        }
        wxOpenService.setWxOpenConfigStorage(redisConfigStorage);
        WxOpenComponentService wxOpenComponentService = wxOpenService.getWxOpenComponentService();
        WxOpenMpService wxOpenMpService = wxOpenComponentService.getWxMpServiceByAppid(appId);
        if (null == wxOpenMpService) {
            throw new DataNotExistException("获取小程序服务WxMaService失败");
        }
        return wxOpenMpService;
    }

    /**
     * 获取当前小程序服务并将注册为bean
     *
     * @param
     * @return
     */
    @Override
    public WxOpenMaService getWxOpenMaService(Long tenantId) {
        WxOpenService wxOpenService = SpringBeanUtil.getBean(WxOpenService.class);
        if(wxOpenService == null) throw new BusinessException("get WxOpenService service error!");
        WxOpenInRedisConfigStorage redisConfigStorage = null;
        String configStoragekey = RedisKeyConstant.WX_OPEN_CONFIG_KEY.concat(tenantId.toString()).concat(":").concat(WxOpenServiceTypeConstant.APPID_TYPE_MINI.toString());
        String appId = null;
        Object objConfigStorage = redisUtils.get(configStoragekey);
        if(ObjectUtils.isEmpty(objConfigStorage)) {
            redisConfigStorage = new WxOpenInRedisConfigStorage(redisProperties.getJedisPool(), "wx:open");
            OpenConfigDTO wxOpenConfig = queryOpenConfigOfWxOpen();
            if(wxOpenConfig == null) throw new BusinessException("没有读取到开放平台配置参数!");
            OpenConfigOfExtend openConfigOfExtend = wxOpenConfig.getOpenConfigOfExtend();
            if(openConfigOfExtend == null) throw new BusinessException("没有读取到开放平台配置参数!");
            OpenConfigOfWxOpen openConfigOfWxOpen = openConfigOfExtend.getOpenConfigOfWxOpen();
            if(openConfigOfWxOpen == null) throw new BusinessException("没有读取到开放平台配置权限!");
            OpenAuthDTO openAuthDTO = queryWxOpenAuthByTenantId(tenantId, wxOpenConfig.getType(), wxOpenConfig.getSubType(), WxOpenServiceTypeConstant.APPID_TYPE_MINI);
            if(openAuthDTO == null) throw new BusinessException("没有读取到开放平台配置权限!");
            OpenAuthOfExtend openAuthOfExtend = openAuthDTO.getOpenAuthOfExtend();
            if(openAuthOfExtend == null) throw new BusinessException("没有读取到开放平台配置权限!");
            appId = openAuthDTO.getAppId();
            OpenAuthOfWxOpen openAuthOfWxOpen = openAuthOfExtend.getOpenAuthOfWxOpen();
            if(openAuthOfWxOpen == null) throw new BusinessException("没有读取到开放平台配置权限!");
            redisConfigStorage.setComponentAppId(wxOpenConfig.getComponentAppId());
            redisConfigStorage.setComponentAppSecret(wxOpenConfig.getOpenAppSecret());
            redisConfigStorage.setComponentToken(wxOpenConfig.getComponentMsgToken());
            redisConfigStorage.setComponentAesKey(wxOpenConfig.getComponentMsgAesKey());
            redisConfigStorage.setComponentVerifyTicket(openConfigOfWxOpen.getComponentVerifyTicket());
            redisConfigStorage.setComponentAccessToken(openConfigOfWxOpen.getComponentAccessToken());
            redisConfigStorage.updateAuthorizerRefreshToken(appId, openAuthOfWxOpen.getAuthorizerRefreshToken());
            WxOpenAuthorizerAccessToken authorizerAccessToken = new WxOpenAuthorizerAccessToken();
            authorizerAccessToken.setAuthorizerRefreshToken(openAuthOfWxOpen.getAuthorizerRefreshToken());
            authorizerAccessToken.setAuthorizerAccessToken(openAuthOfWxOpen.getAuthorizerAccessToken());
            redisConfigStorage.updateAuthorizerAccessToken(appId,authorizerAccessToken);
            redisUtils.set(configStoragekey, redisConfigStorage, 1000);
        }else{
            redisConfigStorage = (WxOpenInRedisConfigStorage) objConfigStorage;
        }
        wxOpenService.setWxOpenConfigStorage(redisConfigStorage);
        WxOpenComponentService wxOpenComponentService = wxOpenService.getWxOpenComponentService();
        WxOpenMaService wxOpenMaService = wxOpenComponentService.getWxMaServiceByAppid(appId);
        if (null == wxOpenMaService) {
            throw new DataNotExistException("获取小程序服务WxMaService失败");
        }
        return wxOpenMaService;
    }

    @Override
    public WxCpService getWxCpService(Long tenantId) {
        WxCpService wxCpService = SpringBeanUtil.getBean(WxCpService.class);
        if (wxCpService == null)  throw new BusinessException("get WxCpService service error!");
        WxCpJedisConfigImpl redisConfigStorage = null;
        String configStoragekey = RedisKeyConstant.WX_CP_CONFIG_KEY.concat(tenantId.toString()).concat(":").concat(WxCpServiceTypeConstant.CP_TYPE_SB.toString());
        Object objConfigStorage = redisUtils.get(configStoragekey);
        if(ObjectUtils.isEmpty(objConfigStorage)) {
            redisConfigStorage = new WxCpJedisConfigImpl(redisProperties.getJedisPool(), "wx:cp");
            OpenConfigDTO wxOpenConfig = queryOpenConfigOfWxCpSb();
            if (wxOpenConfig == null) throw new BusinessException("没有读取到企微供应商平台配置参数!");
            OpenConfigOfExtend openConfigOfExtend = wxOpenConfig.getOpenConfigOfExtend();
            if (openConfigOfExtend == null) throw new BusinessException("没有读取到企微供应商平台配置参数!");
            OpenConfigOfWxCp openConfigOfWxCp = openConfigOfExtend.getOpenConfigOfWxCp();
            if (openConfigOfWxCp == null) throw new BusinessException("没有读取到企微供应商平台配置权限!");
            OpenAuthDTO openAuthDTO = queryWxOpenAuthByTenantId(tenantId, wxOpenConfig.getType(), wxOpenConfig.getSubType(), WxCpServiceTypeConstant.CP_TYPE_SB);
            if (openAuthDTO == null) throw new BusinessException("没有读取到企微应用配置权限!");
            OpenAuthOfExtend openAuthOfExtend = openAuthDTO.getOpenAuthOfExtend();
            if (openAuthOfExtend == null) throw new BusinessException("没有读取到企微应用配置权限!");
            OpenAuthOfWxCpSb openAuthOfWxCpSb = openAuthOfExtend.getOpenAuthOfWxCpSb();
            if (openAuthOfWxCpSb == null) throw new BusinessException("没有读取到企微应用配置权限!");
            redisConfigStorage.setCorpId(openAuthDTO.getComponentAppId());
            redisConfigStorage.setCorpSecret(openAuthDTO.getAppSecret());
            redisConfigStorage.setAesKey(openAuthDTO.getAppAesKey());
            redisConfigStorage.setToken(openAuthDTO.getAppToken());
            redisConfigStorage.setAgentId(Integer.valueOf(openAuthDTO.getAppId()));
            redisConfigStorage.updateAccessToken(openAuthOfWxCpSb.getAccessToken(),TIME.intValue());
            redisUtils.set(configStoragekey, redisConfigStorage, 1000);
        }else{
            redisConfigStorage = (WxCpJedisConfigImpl) objConfigStorage;
        }
        wxCpService.setWxCpConfigStorage(redisConfigStorage);
        if (null == wxCpService) {
            throw new DataNotExistException("获取企业微信服务wxCpService失败");
        }
        return wxCpService;
    }

    @Override
    public SmsService getSmsService(Long tenantId) {
        SmsConfigBO smsConfigBO = smsConfigServiceImpl.queryDefaultConfig();
        if(smsConfigBO == null) throw new BusinessException("没有读取到缺省的短信配置参数!");
        if(smsConfigBO.getSmsVendorCode().equalsIgnoreCase(SmsVendorEnum.SMS_ALI.getCode())) {
            SmsConfigByAliBO smsConfigByAliBO = smsConfigBO.getSmsConfigByAli();
            if(smsConfigByAliBO == null) throw new BusinessException("没有读取到缺省的短信配置参数!");
            SmsProperties smsProperties = new SmsProperties();
            SmsProperties.Aliyun aliyunSmsProperty = new SmsProperties.Aliyun();
            aliyunSmsProperty.setAppid(smsConfigBO.getUserName());
            aliyunSmsProperty.setDomain(smsConfigBO.getSmsHost());
            aliyunSmsProperty.setAccessKey(smsConfigBO.getUserName());
            String encryptPassword = smsConfigBO.getUserPassword();
            if(StringUtils.isNotEmpty(encryptPassword)){
                PasswordCryptor cryptor = new SimplePasswordCryptor();
                String password = cryptor.decrypt(encryptPassword);
                aliyunSmsProperty.setSecurityKey(password);
            }
            smsProperties.setAliyun(aliyunSmsProperty);
            LogUtils.info(">>>>>>>>SmsService Properties:{}", JsonUtils.toJsonStr(smsProperties));
            SmsAutoConfigure.AliSmsServiceConfiguration smsAutoConfigure = new SmsAutoConfigure.AliSmsServiceConfiguration();
            return smsAutoConfigure.aliSmsService(smsProperties);
        }else if(smsConfigBO.getSmsVendorCode().equalsIgnoreCase(SmsVendorEnum.SMS_TECENT.getCode())) {
            SmsConfigByTecentBO smsConfigByTecentBO = smsConfigBO.getSmsConfigByTecent();
            if(smsConfigByTecentBO == null) throw new BusinessException("没有读取到缺省的短信配置参数!");
            SmsProperties smsProperties = new SmsProperties();
            SmsProperties.Tencent tecentSmsProperty = new SmsProperties.Tencent();
            //tecentSmsProperty.setEndpoint(smsConfigByTecentBO.);
            smsProperties.setTencent(tecentSmsProperty);
            SmsAutoConfigure.TentcentSmsServiceConfiguration smsAutoConfigure = new SmsAutoConfigure.TentcentSmsServiceConfiguration();
            return smsAutoConfigure.tencentSmsService(smsProperties);
        }
        return null;
    }

    @Override
    public EmailService getEmailService(Long tenantId) {
        EmailConfigVO emailConfigVO = emailConfigServiceImpl.viewDefaultEmailConfig();
        if(emailConfigVO == null) throw new BusinessException("没有读取到缺省的邮件配置参数!");

        EmailSenderProperties senderProperties = senderPropertiesMap.get(emailConfigVO.getVenderCode());
        EmailService emailService = new EmailServiceImpl();
        if(senderProperties == null){
            senderProperties = new EmailSenderProperties();
            senderProperties.setHost(emailConfigVO.getSendHost());
            senderProperties.setPort(emailConfigVO.getSendPort());
            senderProperties.setUsername(emailConfigVO.getSenderEmail());
            senderProperties.setPassword(emailConfigVO.getSenderEmailPassword());
            senderProperties.setProtocol(emailConfigVO.getSendProtocol());
            senderProperties.setEnable(true);
            senderProperties.setEncoding("UTF-8");
            String sslRequire = emailConfigVO.getOtherInfo();
            if(StringUtils.isNotEmpty(sslRequire)){
                EmailConfigSslBO emailConfigSslBO = JsonUtils.fromJson(sslRequire, EmailConfigSslBO.class);
                if(emailConfigSslBO!=null){
                    Map<String, String> javaMailProperties = new HashMap<>();
                    javaMailProperties.put("mail.smtp.auth", emailConfigVO.getNeedSsl()==1?"true":"false");
                    javaMailProperties.put("mail.smtp.ssl.enable", emailConfigVO.getNeedSsl()==1?"true":"false");
                    javaMailProperties.put("mail.smtp.socketFactory.port", emailConfigVO.getNeedSsl()==1?emailConfigVO.getSendHost():"");
                    javaMailProperties.put("mail.smtp.socketFactory.class", emailConfigVO.getNeedSsl()==1?"com.ibeeking.nematos.msg.email.core.config.MailSSLSocketFactory":"");
                    javaMailProperties.put("mail.smtp.starttls.enable", emailConfigSslBO.getRequireTls()==1?"true":"false");
                    javaMailProperties.put("mail.smtp.starttls.required", emailConfigSslBO.getRequireTls()==1?"true":"false");
                    senderProperties.setProperties(javaMailProperties);
                }
            }
            senderPropertiesMap.put(RedisKeyConstant.EMAIL_SENDER_KEY_PREFIX+emailConfigVO.getVenderCode(),senderProperties);
            PooledEmailSender pooledEmailSender = new PooledEmailSender();
            pooledEmailSender.loadEmailSenderProperties(new ArrayList<>(senderPropertiesMap.values()));
            emailService.initEmailSender(pooledEmailSender);
        }

        return emailService;
    }


    private String decrypt(String encryptStr) {
        try {
            String originStr = EncryptUtils.RsaEncrypt.decrypt(encryptStr, rsaConfig.getKeyPair().getPrivate());
            return originStr;
        } catch (Exception e) {
            throw new BusinessException("请检查公钥是否被修改" + e.toString());
        }
    }
    private OpenServiceImpl getBean() {
        return SpringBeanUtil.getBean(this.getClass());
    }
}